/**
Defines functions used by tcpechotimecli.c for spawning either 'echo' or 'time'
clients.
*/
#include "globals.h"

/**
Wrapper for 'select'. This handles EINTR error which we might get while polling
for descriptors to get ready.
*/
int
Select_handle_intr(
    int nfds, 
    fd_set *readfds, 
    fd_set *writefds, 
    fd_set *exceptfds,
    struct timeval *timeout
    )
{
	int		n;

	if ((n = select(nfds, readfds, writefds, exceptfds, timeout)) < 0)
    {
        //
        // Ignore EINTR.
        //

        if (errno == EINTR)
        {
            log_msg("EINTR in select");
            return 0;
        }
        else
        {
		    err_sys("select error");
        }
    }
	return(n);		/* can return 0 on timeout */
}

/**
Spawns and executes the echo client as a child of 'tcpechotimecli' process.

This function creates a new process for the echo client to run on. The child
process that is created communicates with the parent process using the 'pipe' 
IPC mechanism. The echo client polls both 'stdin' and the socket that connects
it to the server for input data. The data read from 'stdin' is sent to the 
server and the subsequent data read from the server is printed to 'stdout'.

@param pfd supplies the file descriptors for the pipe.
@param ip_addr supplies the IP Address of the server.
*/
void 
exec_echo_client(
    int pfd[2],
    char* ip_addr
    )
{
    ssize_t n;
    char buf[MAXLINE];

    int maxfd_echo;
    fd_set rset_echo;

    char* p_descriptor;
    
    pid_t child_pid;

    if (pipe(pfd) == -1)
    {
        perror("pipe failed.");
        exit(1);
    }

    //
    // 'fork' returns twice. Once to the parent, once to the client.
    //

    if ((child_pid = Fork()) == 0)
    {
        //
        // Child process.
        // Close the read end of the pipe.
        //

        // TODO: close the other end.

        Close(pfd[0]);
        
        //
        // Package the pipe file descriptor to a string. This is sent as a
        // commandline argument to the client. The client converts it back to
        // integer and writes to it.
        //

        p_descriptor = (char*)Malloc(sizeof(char)*MAXLINE);
        bzero(p_descriptor, MAXLINE);
        itoa(pfd[1], p_descriptor);

        //
        // Replace the client image with that of the echo client.
        //

        if ((execlp("xterm", "xterm", "-e", "./echo_cli", ip_addr,\
            p_descriptor, (char *) 0)) < 0)
        {
            log_msg("exec error");
            exit(1);
        }
    }
    else
    {
        //
        // Parent process.
        // Close the write end of the pipe.
        //

        Close(pfd[1]);

        //
        // Loop to read diagnostic messages from the echo client.
        //

        while(TRUE)
        {
            //
            // Initialize 'select' parameters. Indicate that we are interested
            // only with the state of the 'pipe' file. 
            //

            FD_ZERO(&rset_echo);
            FD_SET(pfd[0], &rset_echo);

            maxfd_echo = pfd[0] + 1;
            
            Select_handle_intr(maxfd_echo, &rset_echo, NULL, NULL, NULL);

            if (FD_ISSET(pfd[0], &rset_echo))
            {
                n = Readline(pfd[0], buf, MAXLINE);

                if (n == 0)
                {
                    //
                    // Child terminated. No more messages to be read.
                    //

                    Write(fileno(stdout), DIAG_ECHO_CLIENT_TERM,\
                        strlen(DIAG_ECHO_CLIENT_TERM));

                    break;
                }
                else
                {
                    //
                    // Print the diagnostic message read from the child.
                    //

                    Write(fileno(stdout),DIAG_ECHO_CLIENT_RCVD,\
                        strlen(DIAG_ECHO_CLIENT_RCVD));

                    Write(fileno(stdout), buf, n);
                    bzero(buf, MAXLINE);
                }
            }
        }
    }    
}

/**
Spawns and executes the daytime client as a child of 'tcpechotimecli' process.

This function creates a new process for the daytime client to run on. The child
process that is created communicates with the parent process using the 'pipe' 
IPC mechanism. The daytime client polls the socket that connects it to the 
server for input data. The data read from from the server is printed to 
'stdout'. It can be stopped by pressing ctrl+c as server sends time data
indefinitely as long as the connection persists.

@param pfd supplies the file descriptors for the pipe.
@param ip_addr supplies the IP Address of the server.
*/
void 
exec_time_client(
    int pfd[2],
    char* ip_addr
    )
{
    ssize_t n;
    char buf[MAXLINE];
    int maxfd_echo;

    fd_set rset_echo;
    char* p_descriptor;
    pid_t child_pid;
    
    if (pipe(pfd) == -1)
    {
        perror("pipe failed.");
        exit(1);
    }

    //
    // 'fork' returns twice. Once to the parent, once to the client.
    //

    if ((child_pid = Fork()) == 0)
    {
        //
        // Child process.
        // Close the read end of the pipe.
        //

        Close(pfd[0]);
        
        //
        // Package the pipe file descriptor to a string.
        //

        p_descriptor = (char*)Malloc(sizeof(char)*MAXLINE);
        bzero(p_descriptor, MAXLINE);
        itoa(pfd[1], p_descriptor);

        //
        // Replace the client image with that of the echo client.
        //

        if ((execlp("xterm", "xterm", "-e", "./time_cli", ip_addr,\
            p_descriptor, (char *) 0)) < 0)
        {
            log_msg("exec error");
            exit(1);
        }
    }
    else
    {
        //
        // Parent process.
        // Close the write end of the pipe.
        //

        Close(pfd[1]);

        //
        // Loop to read diagnostic messages from the daytime client.
        //

        while(TRUE)
        {
            //
            // Initialize 'select' parameters. Indicate that we are interested
            // only with the state of the 'pipe' file. 
            //

            FD_ZERO(&rset_echo);
            FD_SET(pfd[0], &rset_echo);

            maxfd_echo = pfd[0] + 1;
           
            Select_handle_intr(maxfd_echo, &rset_echo, NULL, NULL, NULL);

            if (FD_ISSET(pfd[0], &rset_echo))
            {
                n = Readline(pfd[0], buf, MAXLINE);

                if (n == 0)
                {
                    //
                    // Child terminated. No more messages to be read.
                    //

                    Write(fileno(stdout), DIAG_TIME_CLIENT_TERM,\
                        strlen(DIAG_TIME_CLIENT_TERM));
                    break;
                }
                else
                {
                    //
                    // Print the diagnostic message read from the child.
                    //

                    Write(fileno(stdout),DIAG_TIME_CLIENT_RCVD,\
                        strlen(DIAG_TIME_CLIENT_RCVD));
                    Write(fileno(stdout), buf, n);
                    bzero(buf, MAXLINE);
                }
            }
        }
    }
}

